SWAGOLX.EXE (c) 1993 GDSOFT ALL RIGHTS RESERVED 00016 MEMORY/DPMI MANAGEMENT ROUTINES 1 05-28-9313:50ALL SWAG SUPPORT TEAM BIGMEM1.PAS IMPORT 13 > I have seen posts about using Pointer Arrays instead of the standard fixedπ> Arrays. These posts have been helpful but I think the rewriting of an exampleπ> problem would benefit me the best. Please take a look at this simple exampleπ> code:π>π> Program LotsofData;π>π> Type LOData = Array [1..10000] of Real;π>π> Var Value : LOData;π> MaxSizeArray, I, NumElement : Integer;π> NewValue : Real;π>π> beginπ> Write('Please input the Maximum Size of the Array: ');π> Readln(MaxSizeArray);π> For I := 1 to MaxSizeArray Doπ> Value[I] := 0.0;π> Writeln('Array Initialized');π> Writeln;π> Write('Please input the Number of Array Element to Change: ');π> Readln(NumElement);π> Write('Please input the New Number For Value[',NumElement,']: ');π> Readln(NewValue);π> Value[NumElement] := NewValue;π> end.π>ππResponse;π1. Declare the Variable Value as LOData -π e.g. Var Value : LOData;ππ2. Read MaxSizeArray;ππ3. Allocate memory For the Array by using NEW() or GETMEM()π e.g. NEW(Value);π or GetMem(Value, Sizeof(Real) * MaxSizeArray);ππGetmem() is better because you can allocate just the precise amount ofπmemory needed.ππ4. From then on refer to your Array as Valueπ e.g. Value[Element] := NewValue;ππ5. When you finish, deallocate memory byπ [a] Dispose(Value) - if you used NEW() to begin with, orπ [b] FreeMem(Value, Sizeof(Real) * MaxSizeArray) - if you usedπ GetMem() to begin with.ππ 2 05-28-9313:50ALL SWAG SUPPORT TEAM BIGMEM2.PAS IMPORT 10 {πBP7 is not limited to 16Meg of memory, by running the Program below in aπWindows 3.1 Window, it created 744 Objects allocating 30Meg of memory. Theπfinal printout verified that all the items were still there.ππSo if you use a third party DPMI server, you should be able to use all yourπmemory.ππI might point out that I allocated 30Meg of memory on my 16Meg machine. I runπWindows 3.1 With a 32Meg permanent swap File.π}ππProgram BigMemory;πUsesπ OpStrDev,Objects;ππTypeπ PDataType=^DataType;π DataType=Object(tObject)π C:LongInt;π S:String;π Stuffing:Array[1..40000] of Byte;π Constructor Init(I:LongInt);π end;πVarπ Counter:LongInt;π List:TCollection;ππConstructor DataType.Init(I:LongInt);πbeginπ tObject.Init;π C:=I;π Write(tpstr,'I = ',I,' I div 2 =',I div 2);π S:=returnstr;πend;ππProcedure Printall;π Procedure PrintOne(P:PDataType);Far;π beginπ Writeln(P^.C,' - ',P^.S);π end;πbeginπ List.Foreach(@PrintOne);πend;ππbeginπ Counter:=0;π List.Init(1000,1000);π Repeatπ inc(Counter);π List.Insert(New(PDataType,Init(Counter)));π Write(Counter,' mem =',Memavail,^M);π Until Memavail<50000;π PrintAll;πend.π 3 05-28-9313:50ALL SWAG SUPPORT TEAM CMOSSTUF.PAS IMPORT 69 (*******************************************************************)π Program SaveCMOS; { Compiler: Turbo & Quick Pascal }π{ }π{ File name: SaveCMOS.PAS coded: Mar.3.1993, Greg Vigneault }π{ }π{ This utility will read the entire contents of the CMOS RAM, and }π{ save it to a File. Invoke this Program as... }π{ }π{ SAVECMOS <Filename> }π{ }π Uses Crt; { import ReadKey }π Const AddressRTC = $70; { RTC register address latch }π DataRTC = $71; { RTC register data }π AStatusRTC = $0A; { RTC status register A }π Var tempCMOS,π RegCMOS : Byte; { RTC register }π MapCMOS : Array [0..63] of Byte; { RTC CMOS reg map }π OutFile : File; { saved CMOS data }π ch : Char; { For user input }π FResult : Integer; { check File Write }π(*-----------------------------------------------------------------*)π Function ReadCMOS( RegCMOS :Byte ) :Byte;π beginπ RegCMOS := RegCMOS and $3F; { don't set the NMI bit }π if (RegCMOS < AStatusRTC) then { wait For end of update? }π Repeatπ Port[AddressRTC] := AStatusRTC; { read status }π Until (Port[DataRTC] and $80) <> 0; { busy bit }π Port[AddressRTC] := RegCMOS; { tell RTC which register }π ReadCMOS := Port[DataRTC]; { and read in the data Byte }π end {ReadCMOS};π(*-----------------------------------------------------------------*)π Procedure HelpExit;π begin WriteLn; WriteLn( 'Usage: SAVECMOS <Filename>' );π WriteLn( CHR(7) ); Halt(1);π end {HelpExit};π(*-----------------------------------------------------------------*)π beginπ WriteLn; WriteLn( 'SaveCMOS v0.1 Greg Vigneault' ); WriteLn;π if (ParamCount <> 1) then HelpExit;π Assign( OutFile, ParamStr(1) );π {$i-} Reset( OutFile, SizeOf(MapCMOS) ); {$i+}π if (IoResult = 0) then beginπ Repeatπ Write('File ',ParamStr(1),' exists! OverWrite? (Y/N): ',#7);π ch := UpCase( ReadKey ); WriteLn;π Until (ch in ['Y','N']);π if (ch = 'N') then begin WriteLn('ABORTED'); Halt(2); end;π end;π ReWrite( OutFile, SizeOf(MapCMOS) ); WriteLn;π For RegCMOS := 0 to 63 do MapCMOS[RegCMOS] := ReadCMOS(RegCMOS);π MapCMOS[AStatusRTC] := MapCMOS[AStatusRTC] and $7F; { clear UIP }π BlockWrite( OutFile, MapCMOS, 1, FResult );π if (FResult <> 1) then beginπ WriteLn( 'Error writing to ',ParamStr(1),'!',#7 );π Close( OutFile ); Halt(3);π end;π FillChar( MapCMOS, SizeOf(MapCMOS), 0 );π Reset( OutFile, SizeOf(MapCMOS) );π BlockRead( OutFile, MapCMOS, 1, FResult );π if (FResult <> 1) then beginπ WriteLn( 'Error reading from ',ParamStr(1),'!',#7 );π Close( OutFile ); Halt(4);π end;π Close(OutFile);π For RegCMOS := 10 to 63 do begin { don't include time in verify }π if (RegCMOS = AStatusRTC) thenπ MapCMOS[RegCMOS] := MapCMOS[RegCMOS] and $7F;π if (MapCMOS[RegCMOS] <> ReadCMOS(RegCMOS)) then beginπ WriteLn('!!! Error: can''t verify File contents !!!');π WriteLn(#7#7#7#7#7); Halt(5);π end;π end;π WriteLn('! The CMOS RAM has now been saved in ',ParamStr(1),#7);π end {SaveCMOS}.π(*******************************************************************)ππ Greg_ππ Mar.03.1993.Toronto.Canada. greg.vigneault@bville.gts.orgπ---π ■ QNet3ß ■ City2City / 1/0/11 / Baudeville BBS / Toronto / 416-283-0114π<<<>>>πππDate: 03-04-93 (03:03) Number: 127 of 160 (Echo)π To: CHRIS LAUTENBACH Refer#: NONEπFrom: GREG VIGNEAULT Read: 03-05-93 (17:02)πSubj: TP: LOADCMOS SOURCE CODE Status: PUBLIC MESSAGEπConf: C-ProgramMING (368) Read Type: GENERAL (+)ππ(*******************************************************************)π Program LoadCMOS; { Compiler: Turbo & Quick Pascal }π{ }π{ File name: LoadCMOS.PAS coded: Mar.3.1993, Greg Vigneault }π{ }π{ LOADCMOS <Filename> }π{ }π Uses Crt; { import ReadKey }π Const AddressRTC = $70; { RTC register address latch }π DataRTC = $71; { RTC register data }π AStatusRTC = $0A; { RTC status register A }π BStatusRTC = $0B; { RTC status register B }π CStatusRTC = $0C; { RTC status register C }π DStatusRTC = $0D; { RTC status register D }π SecondsRTC = 0; { seconds (BCD, 0..59) }π MinutesRTC = 2; { minutes (BCD, 0..59) }π HoursRTC = 4; { hours (BCD, 0..23) }π WeekDayRTC = 6; { day of week (1..7) }π DayOfMonthRTC = 7; { day of month (BCD, 1..31) }π MonthRTC = 8; { month (BCD, 1..12) }π YearRTC = 9; { year (BCD, 0..99) }π Var RegCMOS : Byte; { RTC register }π MapCMOS : Array [0..63] of Byte; { RTC CMOS reg map }π ChkSumCMOS : Integer; { CMOS checksum }π InFile : File; { saved CMOS data }π ch : Char; { For user input }π FResult : Integer; { check File Write }π(*-----------------------------------------------------------------*)π Procedure WriteCMOS( RegCMOS, Value :Byte );π Var temp : Byte;π beginπ if not (RegCMOS in [0,1,CStatusRTC,DStatusRTC]) thenπ beginπ if (RegCMOS < CStatusRTC) then beginπ Port[AddressRTC] := BStatusRTC;π temp := Port[DataRTC] or $80; { stop the clock}π Port[AddressRTC] := BStatusRTC;π Port[DataRTC] := temp;π end;π Port[AddressRTC] := RegCMOS and $3F; { select reg }π Port[DataRTC] := Value; { Write data }π if (RegCMOS < CStatusRTC) then beginπ Port[AddressRTC] := BStatusRTC;π temp := Port[DataRTC] and not $80; { enable clock }π Port[AddressRTC] := BStatusRTC;π Port[DataRTC] := temp;π end;π end;π end {WriteCMOS};π(*-----------------------------------------------------------------*)π Procedure HelpExit;π begin WriteLn; WriteLn( 'Usage: LOADCMOS <Filename>' );π WriteLn( CHR(7) ); Halt(1);π end {HelpExit};π(*-----------------------------------------------------------------*)π beginπ WriteLn; WriteLn( 'LoadCMOS v0.1 Greg Vigneault' ); WriteLn;π if (ParamCount <> 1) then HelpExit;π Assign( InFile, ParamStr(1) );π {$i-} Reset( InFile, SizeOf(MapCMOS) ); {$i+}π if (IoResult <> 0) then beginπ Write('Can''t find ',ParamStr(1),'!',#7);π Halt(1);π end;π FillChar( MapCMOS, SizeOf(MapCMOS), 0 ); { initialize }π BlockRead( InFile, MapCMOS, 1, FResult ); { saved CMOS }π Close(InFile);π if (FResult <> 1) then beginπ WriteLn('! Error reading File',#7);π Halt(2);π end;π MapCMOS[AStatusRTC] := MapCMOS[AStatusRTC] and $7F;π ChkSumCMOS := 0; { do checksum }π For RegCMOS := $10 to $2Dπ do ChkSumCMOS := ChkSumCMOS + Integer( MapCMOS[RegCMOS] );π if (Hi(ChkSumCMOS) <> MapCMOS[$2E])π or (Lo(ChkSumCMOS) <> MapCMOS[$2F]) then beginπ WriteLn('!!! CheckSum error in ',ParamStr(1) );π WriteLn(#7#7#7#7#7); Halt(2);π end;π For RegCMOS := AStatusRTC to 63π do WriteCMOS( RegCMOS, MapCMOS[RegCMOS] );π WriteLn('! The CMOS RAM has been restored from ',ParamStr(1),#7);π end {LoadCMOS}.π(*******************************************************************)π 4 05-28-9313:50ALL SWAG SUPPORT TEAM DPMIINFO.PAS IMPORT 164 Hello All,ππAgain, interrupts from protected mode. This is an updated version of myπprevious article, which, by the way, generated much less respons (none)πthan I expected. Where are the BTrieve Programmers, the DesqView APIπWriters, the fossil Writers, the .... Maybe they know everythingπalready. Well then, what has been changed?ππ* little bugs fixed (memory not freed, SEG does not work, etc.)π* I stated that if you want to pass parameters on the stack you had toπ do low level stuff. This is not necessary. I do everything in highπ level(?) pascal now.π* Point 5 of the first Type of unsupported interrupts was inComplete.π There's sometimes much more work involved :-(π* A simple Unit is presented, which helps to cut down code size. Seeπ Appendix AππCompiling Real to protected mode has been very simple For most of us.πJust Compile and go ahead. 99.5% of your code works fine. But the otherπ0.5% is going to give you some hard, hard work.π In this article I describe first how I first stuck on the protectedπstone. Than I try to give a general overview of problems one mightπencounter when using interrupts. Next I describe the solutions or giveπat least some hints, and I give a solution to the original Program whichπmade me aware of protected mode conversion problems. Appendix A listsπthe code For a Unit I found usefull when porting my DesqView API toπprotected mode.π References can be found at the end of this article. of course, allπdisclaimers you can come up With apply!πππWhen Compiling a big Program, which supported DesqView, a GP faultπoccurred. It was simple to trace the bug down: TDX would show me theπoffending code. You can get the same error if you try to run theπfollowing Program in protected mode:ππ========cut here==========================πProgram Test;ππFunction dv_win_me : LongInt; Assembler;πAsmπ mov bx,0001hπ mov ah,12hπ int 15h {* push dWord handle on stack *}π pop ax {* pop it *}π pop dx {* and return it *}πend;ππbeginπ Writeln(dv_win_me);πend.π========cut here==========================ππThis little Program must be run under DesqView. When run under DesqViewπit returns the current Window handle on the stack. BUT: when Compiledπunder protected mode NO dWord handle is returned on the stack. So aπstack fault occurs.ππWhat happened? I stuck on one of those unsupported interrupts. Onlyπsupported interupts guarantee to return correct results. You can find aπlist of all supported interrupts in the Borland Open ArchitectureπHandboek For Pascal, Chapter 2 (seperate sold by Borland, not includedπin your BP7 package). Supported are the general Dos and Bios interrupts.ππBeFore eleborating on supported and unsupported interrupts, I have toπexplain a few issues which are probably new to us Pascal Programmers.πWhenever a user interrupt occurs in protected mode (you issue a int xxπcall) Borlands DPMI Extender switches to Real mode, issues theπinterrupt, and switches back to protected mode.ππThis works find For most Cases: interrupts which only pass registerπparameters work fine. But what happens if you, For example, called theπPrint String Function? (int 21h, ah=09h). You pass as parameters ds:dxπpointing to the String to be printed. But, be aware: in protected modeπds contains not a segment but a selector! and the selector in dsπprobably points to an area above the 1MB boundary. These two things areπgoing to give Real mode Dos big, big problems. Don't even try it!π So Borland's DPMI Extender does more than just switching fromπprotected to Real mode when an interrupt occurs: it translates selectorsπto segments when appropriate. But, it can only do so For interrupts itπKNOWS that they need a translation. Such interrupts are calledπsupported. Interrupts about which Borland's DPMI Extender does not knowπabout are unsupported. and they are going to give you Real problems!ππSo you see, when only data is passed in Registers, everything worksπfine. But if you need to pass Pointers, there is a problem. But why didπthe above Program not work? It didn't use selectors you might ask. Well,πthere is another set of interrupts that are unsupported: those thatπexpect or return values on the stack. This is the Case With the aboveπProgram.ππSo, to conclude:π* supported interruptsπ - simple parameter passing using Registers, no segments/selectorsπ or stacks includedπ - interrupts which Borland's DPMI Extender knows about (too few Forπ most of us)π* unsupported interruptsπ - using segments/selectorsπ - involving stacksππIn the next two sections I will fix both Types of problems. I make useπof the DPMI Unit, which comes With the Open Architecture Handbook. Youπdo not need this Unit. As this DPMI Unit is just a wrapper around theπDPMI interrupt 31h, simply looking the interrupts up in Ralph Brown'sπinterrupts list and writing Functions/Procedures For them, works fine.πππUnsupported interrupts which need segmentsπ------------------------------------------ππBecause the data segment and stack segment reside in protected mode, youπneed to allocate memory in Real mode, copy your data (which residesπabove 1MB) and issue the interrupt by calling the DPMI Simulate RealπInterrupt. So our to-do list is:π1) allocate Real mode memoryπ2) copy data from protected mode to Real modeπ3) set up the Real mode Registersπ4) issue interruptπ5) examine resultsππ1) You can allocate Real mode memory by issueing a GlobalDosAlloc (notπ referenced in the online help, but you can look it up in theπ Programmer's refercence manual) request. The GlobalDosAlloc is in theπ WinApi Unit. For example:ππ Uses WinAPI;π Varπ Return : LongInt;π MemSize : LongInt;π beginπ MemSize := 1024;π Return := GlobalDosAlloc(MemSize);π end;ππ This call allocates a block of memory, 1K in size, below the 1MBπ boundary. The value in Return should be split in LongRec(Return).Loπ and LongRec(Return).Hi. The Hi-order Word contains the segment baseπ address of the block. The low-order Word contains the selector Forπ the block.ππ2) You use the selector to acces the block from protected mode and youπ use the segment of the block to acces the block within Real mode (yourπ interrupt).π For example: we want to exchange messages With some interrupt. Theπ code For this would be:π Uses WinAPI;π Varπ Return : LongInt;π MemSize : LongInt;π RealModeSel : Pointer;π RealModeSeg : Pointer;π Message : String;π beginπ MemSize := 256;π Return := GlobalDosAlloc(MemSize);π PtrRec(RealModeSel).seg := LongRec(Return).Lo;π PtrRec(RealModeSel).ofs := 0;π PtrRec(RealModeSeg).seg := LongRec(Return).Hi;π PtrRec(RealModeSeg).ofs := 0;ππ {* Both RealModeSel(ector) and RealModeSeg(ment) point to the sameππ physical address now. *}ππ {* move message from protected mode memory to the allocated selector *}π Message := 'How are your?';π Move(Message, RealModeSel^, Sizeof(Message));ππ {* issue interupt, explained below *}π { <..code..> }π {* the interrupt returns a message *}ππ {* move interrupt's message below 1MB to protected mode *}π Move(RealModeSel^, Message, Sizeof(Message));π Writeln(Message); {* "yes, I'm fine. Thank you!" *}π end;ππ3) We will now examine how to setup an interrupt For Real mode. Most ofπ the time this is transparantly done by Borland's DPMI Extender, butπ we are on our own now. to interrupt Dos, we use the DPMI Functionπ 31h, 0300h. This interrupt simulates an interrupt in Real mode.ππ The Simulate Real Mode Interrupt Function needs a Real mode registerπ data structure. We pass the interrupt and the Real mode register dataπ structure to this Function, which will than start to simulate theπ interrupt.π This Function switches to Real mode, copies the contents of theπ data structure into the Registers, makes the interrupt, copies theπ Registers back into the supplied data structure, switches theπ processor back to protected mode and returns. Voila: you are inπ control again.π Maybe you ask: why need I to setup such a data structure? Why canπ I not simply pass Registers? Several reasons exist, but take Forπ example the RealModeSeg of the previous example. You cannot simplyπ load a RealModeSeg in a register. Most likely a segment violationπ would occur (referring to a non existing segment or you do not haveπ enough rights etc.). ThereFore only in Real mode can Real modeπ segments be loaded.ππ The data structure to pass Registers between protected and Realπ mode can be found in the DPMI Unit which I Repeat here:ππ Typeπ TRealModeRegs = Recordπ Case Integer ofπ 0: (π EDI, ESI, EBP, EXX, EBX, EDX, ECX, EAX: LongInt;π Flags, ES, DS, FS, GS, IP, CS, SP, SS: Word);π 1: (π DI,DIH, SI, SIH, BP, BPH, XX, XXH: Word;π Case Integer ofπ 0: (π BX, BXH, DX, DXH, CX, CXH, AX, AXH: Word);ππ 1: (π BL, BH, BLH, BHH, DL, DH, DLH, DHH,π CL, CH, CLH, CHH, AL, AH, ALH, AHH: Byte));π end;ππ This looks reasonably Complex, doesn't it! More simply is theπ following structure (found in, For example, "Extending Dos" by Rayπ Duncan e.a.)π offset Lenght Contentsπ 00h 4 DI or EDIπ 04h 4 SI or ESIπ 08h 4 BP or EBPπ 0Ch 4 reserved, should be zeroπ 10h 4 BX or EBXπ 14h 4 DX or EDXπ 18h 4 CX or ECXπ 1Ch 4 AX or EAXπ 20h 2 CPU status flagsπ 22h 2 ESπ 24h 2 DSπ 26h 2 FSπ 28h 2 GSπ 2Ah 2 IP (reserved, ignored)π 2Ch 2 CS (reserved, ignored)π 2Eh 2 SP (ignored when zero)π 30h 2 SS (ignored when zero)ππ In the following example, I set the Registers For the above messageπ exchanging Function. It's best to clear all Registers (or at leastπ the SS:SP Registers) beFore calling the Simulate Real Mode Interrupt.ππ Uses DPMI;π Varπ Regs : TRealModeRegs;π beginπ FillChar(Regs, Sizeof(TRealModeRegs), #0); {* clear all Registers *}ππ With Regs do beginπ ah := $xx;π es := PtrRec(RealModeSeg).Seg;π di := PtrRec(RealModeSeg).ofsπ end; { of With }π end;ππ All this is fairly standard. Just set up the Registers you interruptsπ expect, very much like the Intr Procedure.ππ4) We can now issue the interrupt in Real mode using the RealModeIntπ Procedure (in the DPMI Unit). Its definition isππ Procedure RealModeInt(Int: Byte; Var Regs: TRealModeRegs);ππ or you can call int 31h, Function 0300h, see Ralph Brown's interruptπ list.π For our message exchanging Program it would simply be:π RealModeInt(xx, Regs);ππ5) Examine the results. Modified Registers are passed in the Regs dataπ structure so you can check the results.π It is necessary to discriminate between to Types of returnedπ segments. In the example above, I assumed that the Interrupt returnedπ data in the allocated memory block. I already have a selector Forπ that block, so I can examine the results.π Another Type of interrupt returns Pointers to segments it hasπ allocated itself. As we don't have a selector For that memory blockπ we have to create one. We need the following Functions:π - AllocSelectors, to allocate a selectorπ - SetSelectorBase, to let it point to a physical addressπ - SetSelectorLimit, to set the sizeπ An example For this situation: Assume that a certain interruptπ returns a Pointer to a memory area. This Pointer is in es:di.π Register cs contains the size of that memorya rea. I show you how toπ acces that segment.ππ Uses DPMI;π Varπ Regs : TRealModeRegs;π p : Pointer;π beginπ {* setup Regs *}π {* issue interrupt, returning es:di *}ππ {* as we don't have a selector, create one *}π PtrRec(p).Seg := AllocSelectors(1);π PtrRec(p).ofs := 0;ππ {* this selector points to no physical address and has size 0 *}π {* so let the selector point to es:di *}π SetSelectorBase(PtrRec(p).Seg, Regs.es*16+Regs.di);ππ {* Forgive me! This was a joke. The last statement does not work *}π {* of course. Regs.es*16+Regs.di will in the best Cases ({$R+,Q+}) *}π {* result in an overflow error. You have to Write: *}π SetSelectorBase(PtrRec(p).Seg, Regs.es*LongInt(16)+Regs.di);ππ {* the selector now points to a memory area of size 0 *}π SetSelectorLimit(PtrRec(p).Seg, Regs.cx);ππ {* we don't have to set the accesrights (code/data, read/Write, etc. *}π {* as they are almost ok *}ππ {* we can now acces this memory using selector p *}π { <acces block> }ππ {* after using it, free selector *}π FreeSelector(PtrRec(p).Seg);π end;πππAre there any questions? No? Let's go ahead than to the next Type ofπinterrupts.ππUnsupported interrupts which use the stackπ------------------------------------------πThe second Type of unsupported interrupts are the ones which make use ofπthe stack. We can distinguish between:π1. interrupts which need parameters on the stackπ2. interrupts which return parameters on the stackππ1) For the first Type we need to setup a stack. There is an extraπ Compilication, which I had not told yet. As the stack in protectedπ mode resides in a protected mode segment it is unusable For the Realπ mode interrups. So Borland's DPMI Extender switches from theπ protected to a Real mode stack (and back). We can supply a defaultπ Real mode stack if we set the stack Registers (ss and sp) in the Realπ mode register data structure to zero. else it is assumed that ss:spπ points to a Real mode stack. Failure to set them up properly couldπ have disastrous results!ππ We will have to do:π 1) create a Real mode stack using GlobalDosAllocπ 2) fill this stack With valuesπ 3) set ss and sp properlyπ 4) issue interruptππ All in one example Program. The following Program sets DesqView'sπ mouse on a given location on the screen. The supplied handle is theπ handle of the mouse. As DesqView needs dWord values on the stack Iπ allocated a LongIntArray stack which is defined as:ππ Constπ MaxLongIntArray = 1000;π Typeπ PLongIntArray = ^TLongIntArray;π TLongIntArray = Array [0..MaxLongIntArray] of LongInt;ππ The example Program:ππ Procedure SetMouse(Handle, x, y : LongInt);π Constπ StackSize = 3*Sizeof(LongInt);π Varπ Regs : TRealModeRegs;π Stack : PLongIntArray;π l : LongInt;π beginπ {* clear all Registers *}π FillChar(Regs, Sizeof(TRealModeRegs), 0);ππ {* setup the Registers *}π Regs.ax := $1200;π Regs.bx := $0500;ππ {* allocate the stack *}π l := GlobalDosAlloc(StackSize);ππ {* set stacksegment register sp. ss should be set to the bottom of *}π {* the stack = 0 *}π Regs.sp := LongRec(l).Hi;π Stack := Ptr(LongRec(l).Lo, 0);ππ {* fill the stack *}π Stack^[0] := Handle;π Stack^[1] := y;π Stack^[2] := x;ππ {* issue the interrupt *}π RealModeInt($15, Regs);ππ {* free the stack *}π GlobalDosFree(PtrRec(Stack).Seg);π end;ππ2) Looks much like solution above. if only values are returned on theπ stack. Don't Forget to set sp to the top of the stack. In the aboveπ example settings Regs.sp := StackSize;π An example is given below, where a solution to my originalπ problem is given.πππSolution For the dv_win_me Procedure:ππ Uses DVAPI, Objects, WinApi, WinTypes, DPMI;ππ Function dv_win_me : LongInt;π Constπ StackSize = Sizeof(LongInt);π Varπ Regs : TRealModeRegs;π RealStackSeg : Word;π RealStackSel : Word;π l : LongInt;π beginπ {* clear all Registers *}π FillChar(Regs, Sizeof(TRealModeRegs), #0);ππ {* allocate a 1 dWord stack *}π l := GlobalDosAlloc(StackSize);π RealStackSeg := LongRec(l).Hi;π RealStackSel := LongRec(l).Lo;ππ {* clear the stack (not necessary) *}π FillChar(Ptr(RealStackSel, 0)^, StackSize, #0);ππ {* set Registers *}π With Regs do beginπ bx := $0001;π ah := $12;π ss := RealStackSeg;π sp := StackSize;π end; { of With }ππ {* perForm Real mode interrupt *}π RealModeInt($15, Regs);π dv_win_me := PLongInt(Ptr(RealStackSel, 0))^;ππ {* free the stack *}π GlobalDosFree(PtrRec(RealStackSel).Seg);π end;ππ beginπ Writeln(dv_win_me);π end.πππYou see, code size bloats in protected mode! (ThereFore Borland gave usπ16MB....)πππAppendix A.π-----------ππAs promised, some routines I found usefull when working With Real modeπsegments.ππ====================cut here====================πUnit DPMIUtil;ππInterfaceππUses Objects, DPMI;ππConstπ MaxLongIntArray = 1000;πTypeπ{* this Type is usefull For DesqView stacks *}π PLongIntArray = ^TLongIntArray;π TLongIntArray = Array [0..MaxLongIntArray] of LongInt;πππ{* clear all Registers to zero *}ππProcedure ClearRegs(Var Regs : TRealModeRegs);ππ{* allocate memory using GlobalDosAlloc and split the returned *}π{* LongInt into a protected mode Pointer and a Real mode segment *}ππFunction XGlobalDosAlloc(Size : LongInt; Var RealSeg : Word) : Pointer;ππ{* free memory *}ππProcedure XGlobalDosFree(p : Pointer);πππImplementationππUses WinAPI;ππProcedure ClearRegs(Var Regs : TRealModeRegs);πbeginπ FillChar(Regs, Sizeof(TRealModeRegs), 0);πend;ππFunction XGlobalDosAlloc(Size : LongInt; Var RealSeg : Word) : Pointer;πVarπ l : LongInt;πbeginπ l := GlobalDosAlloc(Size);π RealSeg := LongRec(l).Hi;π XGlobalDosAlloc := Ptr(LongRec(l).Lo, 0);πend;ππProcedure XGlobalDosFree(p : Pointer);πbeginπ GlobalDosFree(PtrRec(p).Seg);πend;ππend. { of Unit DPMIUtil }π====================cut here====================πππExample code how to use it. The above dv_win_me routine would look like:ππ Uses DVAPI, Objects, WinApi, WinTypes, DPMI;ππ Function dv_win_me : LongInt;π Constπ StackSize = Sizeof(LongInt);π Varπ Regs : TRealModeRegs;π Stack : PLongIntArray;π beginπ {* clear all Registers *}π ClearReges(Regs);ππ {* allocate a 1 dWord stack *}π Stack := XGlobalDosAlloc(StackSize, Regs.ss);ππ {* set Registers *}π Regs.bx := $0001;π Regs.ah := $12;π Regs.sp := StackSize;ππ {* perForm Real mode interrupt *}π RealModeInt($15, Regs);π dv_win_me := Stack^[0];ππ {* free the stack *}π XGlobalDosFree(Stack);π end;ππ beginπ Writeln(dv_win_me);π end.ππCompare this to the previous code. It just looks a bit prettierπaccording to my honest opininion.πππConclusionπ----------ππAs you saw, the switch from Real to protected mode may be ratherπpainfull. I hope With the above examples and explanations you can makeπit a bit more enjoyable. One question remains: why did Borland notπclearly told us so? Why not present a few examples, warnings, etc.?πMaybe RiChard Nelson can answer this questions For us. Everything heπsays is his private opinion of course, but a look in the kitchen couldπbe worthWhile.ππif you still have questions, I'm willing to answer them in eitherπusenet's Comp.LANG.PASCAL or fidonet's PASCAL.028 or PASCAL. I can'tπport your library of course but if the inFormation presented here is notπenough, just ask.ππππReferencesπ----------π- The usual Borland set of handbooksππ- "Borland Open Architecture Handbook For Pascal", sold separately byπBorland,π 184 pages.ππ- "Extending Dos, a Programmer's Guide to protected-mode Dos", Rayπ Duncan, Charles Petzold, andrew Schulman, M. Steven Baker, Ross P.π Nelson, Stephen R. Davis and Robert Moote. Addison-Wesly, 1992.π ISBN: 0-201-56798-9ππ- "PC Magazine Programmer's Technical Reference: The Processor andπ Coprocessor", Robert L. Hummel. Ziff-Davis Press, 1992.π ISBN: 1-56276-016-5πππ { Dunno if this came before or after this message :) }ππHello Protectors,ππof course, a few hours after my message has been released to the net,πbugfixes seem necessary )-:ππSome minor bugfixes:ππ* In the example about allocating memory below the 1MB, memory isπ allocated but not released. As we have only 1MB down their, this canπ become a problem ;-)π Fix: adding the statementπ GlobalDosFree(RealModeSel);π will clean things upππ * The solution to interrupts which requires parameters passed on theπ stack has a bug. Theπ les di,Regsπ statement does not work of course. Replace byπ mov di,ofFSET Regsπ mov dx,SEG Regsπ mov es,dxπ This does not work when Regs is declared in the stack segment (wellπ done Borland....), you encounter bug number 16, just as I did.... (seeπ next message)πππ 5 05-28-9313:50ALL SWAG SUPPORT TEAM FASTMEM.PAS IMPORT 36 Unit MEM16;π{π Author: Paul VanderSpekπ Date: 03-20-1993ππ This source code is being released as Free-Ware. You may useπ this code in your Programs and modify it to fit your needs. Theπ only restrictions are that you may not distribute the sourceπ code in modified Form or Charge For the source code itself.πππ}ππInterfaceππProcedure Move16(Var Source,Dest;Count:Word);πProcedure FillChar16(Var X; Count: Word; Value:Byte);ππImplementationππProcedure Move16(Var Source,Dest;Count:Word); Assembler;πAsmπ PUSH DSπ LDS SI,SOURCEπ LES DI,DESTπ MOV AX,COUNTπ MOV CX,AXπ SHR CX,1π REP MOVSWπ TEST AX,1π JZ @endπ MOVSBπ@end:POP DSπend;ππProcedure FillChar16(Var X; Count: Word; Value:Byte); Assembler;πAsmπ LES DI,Xπ MOV CX,COUNTπ SHR CX,1π MOV AL,ValUEπ MOV AH,ALπ REP StoSWπ TEST COUNT,1π JZ @endπ StoSBπ@end:πend;ππend.ππ{πThese routines are twice as fast as the normal Move and FillChar routinesπsInce they use MOVSW and StoSW instead of MOVSB and StoSB. They work inπexactly the same way, so you can just replace Move and FillChar With them.π}ππ{π> This source code is being released as Free-Ware. You may useπ> this code in your Programs and modify it to fit your needs. Theπ> only restrictions are that you may not distribute the sourceπ> code in modified form or Charge For the source code itself.ππI'm sorry to say that I'm not impressed, since hundreds of people already haveπinvented this wheel. Besides, your move routine has at least one serious flaw:πit assumes that source and destinaton do not overlap. Which is not always theπcase; if you have a Variable of the Type String as the source, and you want toπcopy a few Characters furtheron in this Variable, you'll mess up the result.ππ> SHR CX,1π> REP MOVSWπ> TEST AX,1π> JZ @endπ> MOVSBπ> @end:POP DSππThe TEST AX, 1 instruction is superfluous. If the number of Bytes in the CXπregister is odd, the SHR CX, 1 instruction will set the carry bit. It's moreπconvenient to test this bit. Here's how:ππ SHR CX, 1π JNC @1π MOVSBπ REP MOVSWπ @1:ππ> Have Fun,ππNo fun if source and destination overlap, as said earlier. Here follows aπmemory move routine With 16-bit moves and overlap check:π}πProcedure MoveMem(Var source, target; size : Word); Assembler;ππAsmπ PUSH DSπ LDS SI, sourceπ LES DI, targetπ MOV CX, sizeπ CLDππ { If an overlap of source and target could occur,π copy data backwards }ππ CMP SI, DIπ JAE @2ππ ADD SI, CXπ ADD DI, CXπ DEC SIπ DEC DIπ STDππ SHR CX, 1π JAE @1π MOVSBπ@1: DEC SIπ DEC DIπ JMP @3ππ@2: SHR CX, 1π JNC @3π MOVSBππ@3: REP MOVSWπ POP DSπend; { MoveMem }πππ{π> For I := 0 to 200 doπ> Move(Buffer,Mem[$A000:0000],320);ππLooks weird to me. Why moving all that stuff 200 times to the first lineπof the screen ?ππ> For I := 100 to 200 doπ> Move(Buffer[320*I],Mem[$A000:(I*320)],320);ππThis could be done viaππMove(Buffer[320*StartLine], Mem[$a000:320*StartLine], 320*NumberOfLines) ;ππwhich should somehow be faster.ππAlso note that TP's Move Procedure Uses a LODSB instruction, which isπtwice as slow as a LODSW instruction on 286+ computers, With big buffers.πSo here is a replacement Move proc, which works fine EXCEPT if the twoπbuffers overlap and destination is at a greater address than source, whichπanyway is not the Case here.π}πProcedure FastMove(Var Src, Dst ; Cnt : Word) ;πAssembler ;πAsmπ Mov DX, DS { Sauvegarde DS }π Mov CX, Cntπ LDS SI, Srcπ LES DI, Dstπ ClD { A priori, on va du dbut vers la fin }π ShR CX, 1 { On va travailler sur des mots }π Rep MovSW { Copie des mots }π JNC @Done { Pas d'octet restant (Cnt pair) ? }π MovSB { Copie dernier octet }π@Done:π Mov DS, DX { Restauration DS }πend ;π{πWell, just a note : this proc works twice faster than TP's Move _only_ ifπSrc and Dst are Word aligned, which is the Case if :π- they are Variables allocated on the heap,π- they are declared in the stack,π- $a+ is specified,π- you use it as described in your examples of code :-)π} 6 05-28-9313:50ALL SWAG SUPPORT TEAM FREEMEM1.PAS IMPORT 4 {πDoes anyone have any routines to find the available memory outside of theπheap ?π}ππFunction GetFreeMemory : LongInt;πVarπ Regs : Registers;πbeginπ Regs.AH := $48;π Regs.BX := $FFFF;π Intr($21,Regs);π GetFreeMemory := LongInt(Regs.BX)*16;πend;π{ππThis Procedure tries to allocate 1MB memory (what's impossible).πDos will give you the maximum of free memory back.π} 7 05-28-9313:50ALL SWAG SUPPORT TEAM GETCACHE.PAS IMPORT 34 {π> Well is there a way to find out if Norton Cache is installed?ππTest For SmartDrv.* , HyperDsk only. ! Others Untested !π}ππProgram IsThereAnyCache;πUsesπ Dos;ππConstπ AktCache : Byte = 0;π CacheNames : Array[0..10] of String[25] = (π '*NO* Disk-Cache found','SmartDrv.Exe','SmartDrv.Sys',π 'Compaq SysPro','PC-Cache V6.x','PC-Cache V5.x',π 'HyperDsk ?', 'NCache-F','NCache-S',π 'IBMCache.Sys','Q-Cache (?)');ππVarπ Version : Integer;π Regs : Registers;ππFunction SmartDrvVersion:Integer;πVarπ Bytes : Array[0..$27] of Byte; { return Buffer }π TFile : Text;πbeginπ SmartDrvVersion := -1; { assume NO smartdrv ! }π {--------Check For SmartDrv.EXE---------- }π FillChar( Regs, Sizeof(Regs), 0 );π Regs.AX := $4A10; { install-check }π Intr( $2F, Regs );π if Regs.FLAGS and FCARRY = 0 then { OK! }π beginπ if Regs.AX = $BABE then { the MAGIC-# }π beginπ SmartDrvVersion := Integer(Regs.BP);π AktCache := 1;π Exit;π end;π end;π { -------Check For SmartDrv.SYS----------- }π Assign(TFile,'SMARTAAR');π {$I-}π Reset(TFile);π {$I+}π if IOResult <> 0 thenπ Exit; { No SmartDrv }π FillChar( Regs, Sizeof(Regs), 0 );π Regs.AX := $4402; { IoCtl }π Regs.BX := TextRec(TFile).Handle;π Regs.CX := Sizeof(Bytes);π Regs.DS := Seg(Bytes);π Regs.DX := Ofs(Bytes);π MsDos(Regs); { int 21h }π Close(TFile);π if Regs.FLAGS and FCARRY <> 0 thenπ Exit; { Error-# in Regs.AX ...}π SmartDrvVersion := Bytes[$E] + 256 * Bytes[$F];π AktCache := 2;πend;ππFunction CompaqPro : Integer;πbeginπ CompaqPro := -1;π Regs.AX := $F400;π Intr($16, Regs);π if Regs.AH <> $E2 thenπ Exit;π if Regs.AL in[1,2] thenπ AktCache := 3;π CompaqPro := $100;πend;ππFunction PC6 : Integer; { PCTools v6, v5 }πbeginπ PC6 := -1;π Regs.AX := $FFA5;π Regs.CX := $1111;π Intr($16, Regs);π if Regs.CH <> 0 thenπ Exit;π PC6 := $600;π AktCache := 4;πend;ππFunction PC5 : Integer;πbeginπ PC5 := -1;π Regs.AH := $2B;π Regs.CX := $4358; {'CX'}π Intr($21, Regs);π if Regs.AL <> 0 thenπ Exit;π PC5 := $500;π AktCache := 5;πend;ππFunction HyperDsk : Integer; { 4.20+ ... }πbeginπ Hyperdsk:= -1;π Regs.AX := $DF00;π Regs.BX := $4448; {'DH'}π Intr($2F, Regs);π if Regs.AL <> $FF thenπ Exit;π if Regs.CX <> $5948 thenπ Exit; { not a "Hyper" product }π HyperDsk := Regs.DX;π AktCache := 6;πend;ππFunction Norton : Integer;πbeginπ Norton := -1;π Regs.AX := $FE00;π Regs.DI := $4E55; {'NU'}π Regs.SI := $4353; {'CS' test For Ncache-S v5 }π Intr($2F, Regs);π if Regs.AH = $00 thenπ beginπ Norton := $500;π AktCache := 7;π Exit;π end;π { Test For Ncache-F v5 / v6 }π Regs.AX := $FE00;π Regs.DI := $4E55; {'NU'}π Regs.SI := $4353; {'CF' test For Ncache-F v5, V6+ }π Intr($2F, Regs);π if Regs.AH <> $00 thenπ Exit;π Norton := $600;π AktCache := 8;πend;ππFunction IBM : Integer;πbeginπ IBM:= -1;π Regs.AX := $1D01;π Regs.Dl := $2; { drive C: }π Intr($13, Regs);π if Regs.Flags and FCarry <> 0 thenπ Exit;π { ES:(BX+$22) -> ASCII-Version-# }π Inc( Regs.BX, $22 );π Regs.AH := (Mem[Regs.ES : Regs.BX] - $30 ) shl 4;π Regs.AH := Regs.AH or (Mem[Regs.ES : Regs.BX + 1] - $30 );π Regs.AL := (Mem[Regs.ES : Regs.BX + 2] - $30 ) shl 4;π Regs.AL := Regs.AL or (Mem[Regs.ES : Regs.BX + 3] - $30 );π IBM := Regs.AX;π AktCache := 9;πend;ππFunction QCache : Integer;πbeginπ QCache := -1;π Regs.AH := $27;π Regs.BX := 0;π intr($13,Regs);π if Regs.BX = 0 thenπ Exit;π QCache := Regs.BX; { ??? }π AktCache := 10;πend;ππbeginπ Writeln('DISK-CACHE-CHECK v1.00 Norbert Igl ''1/93');π Version := SmartDrvVersion;π if Aktcache = 0 thenπ Version := Hyperdsk;π if Aktcache = 0 thenπ Version := Norton;π if Aktcache = 0 thenπ Version := PC6;π if Aktcache = 0 thenπ Version := PC5;π if Aktcache = 0 thenπ Version := IBM;π if Aktcache = 0 thenπ Version := QCache;π if Aktcache = 0 thenπ Version := CompaqPro;ππ Write(CacheNames[AktCache]);π if AktCache <> 0 thenπ Writeln(' (V', Version div 256, '.', Version mod 256, ') installed.');π Writeln;πend.π 8 05-28-9313:50ALL SWAG SUPPORT TEAM MALLOC.PAS IMPORT 28 {$S-,R-,V-,I-,N-,B-,F-}ππ{$IFNDEF Ver40}π{Allow overlays}π{$F+,O-,X+,A-}π{$ENDIF}ππUNIT MemAlloc;ππ { Purpose is to provide the ability to create (destroy) dynamic variables }π { without needing to reserve heap space at compile time. }ππINTERFACEππFUNCTION Malloc(VAR Ptr; Size : Word) : Word;π { Allocate free memory and return a pointer to it. The amount of memory }π { requested from DOS is calculated as (Size/4)+1 paragraphs. If the }π { allocation is successful, the untyped VAR parameter Ptr will be populated }π { with the address of the allocated memory block, and the function will return}π { a zero result. Should the request to DOS fail, Ptr will be populated with }π { the value NIL, and the function will return the appropriate DOS error code. }ππFUNCTION Dalloc(VAR Ptr) : Word;π { Deallocate the memory pointed to by the untyped VAR parameter Ptr }ππIMPLEMENTATIONππ FUNCTION Malloc(VAR Ptr; Size : Word) : Word;π BEGINπ INLINE(π $8B / $46 / <Size / { mov ax,[bp+<Size]}π $B9 / $04 / $00 / { mov cx,4}π $D3 / $E8 / { shr ax,cl}π $40 / { inc ax}π $89 / $C3 / { mov bx,ax}π $B4 / $48 / { mov ah,$48}π $CD / $21 / { int $21 ;Allocate memory}π $72 / $07 / { jc AllocErr ;If any errors ....}π $C7 / $46 / $FE / $00 / $00 / {NoErrors: mov word [bp-2],0 ;Return 0 for successful allocation}π $EB / $05 / { jmp short Exit}π $89 / $46 / $FE / {AllocErr: mov [bp-2],ax ;Return error code}π $31 / $C0 / { xor ax,ax ;Store a NIL value into the ptr}π $C4 / $7E / <Ptr / {Exit: les di,[bp+<Ptr] ;Address of pointer into es:di}π $50 / { push ax ;Save the Segment part}π $31 / $C0 / { xor ax,ax ;Offset is always 0}π $FC / { cld ;Make sure direction is upward}π $AB / { stosw ;Store offset of memory block}π $58 / { pop ax ;Get back segment part}π $AB); { stosw ;Store segment of memory block}π π END {Malloc} ;ππ FUNCTION Dalloc(VAR Ptr) : Word;π BEGINπ IF Pointer(Ptr) <> NIL THEN BEGINπ INLINE(π $B4 / $49 / { mov ah,$49}π $C4 / $7E / <Ptr / { les di,[bp+<Ptr]}π $26 / $C4 / $3D / { es: les di,[di]}π $CD / $21 / { int $21}π $72 / $02 / { jc Exit}π $31 / $C0 / {NoError: xor ax,ax}π $89 / $46 / $FE); {Exit: mov [bp-2],ax}π Pointer(Ptr) := NIL;π END {if} ;π END {Dealloc} ;ππEND {Unit MemAlloc} .ππ 9 05-28-9313:50ALL SWAG SUPPORT TEAM MEMALLOC.PAS IMPORT 37 {This is a Unit MEMALLOC.PAS For use With the .VOC player...}πUnit MemAlloc;ππ{ Purpose is to provide the ability to create (destroy) dynamic Variables }π{ without needing to reserve heap space at Compile time. }ππInterfaceππFunction Malloc(Var Ptr; Size : Word) : Word;π{ Allocate free memory and return a Pointer to it. The amount of memoryπ{ requested from Dos is calculated as (Size/4)+1 paraGraphs. if theπ{ allocation is successful, the unTyped Var parameter Ptr will be populatedπ{ With the address of the allocated memory block, and the Function will return}π{ a zero result. Should the request to Dos fail, Ptr will be populated withπ{ the value NIL, and the Function will return the appropriate Dos error code.π}ππFunction Dalloc(Var Ptr) : Word;π{ Deallocate the memory pointed to by the unTyped Var parameter Ptrπ}ππFunction DosMemAvail : LongInt;π{ Return the size of the largest contiguous chuck of memory available For useπ}ππ{ ---------------------------------------------------------------------------π}ππImplementationππ{ ---------------------------------------------------------------------------π}ππFunction Malloc(Var Ptr; Size : Word) : Word;πbeginπ Inline(π $8B/$46/<SIZE/ { mov ax,[bp+<Size]}π $B9/$04/$00/ { mov cx,4}π $D3/$E8/ { shr ax,cl}π $40/ { inc ax}π $89/$C3/ { mov bx,ax}π $B4/$48/ { mov ah,$48}π $CD/$21/ { int $21 ;Allocate memory}π $72/$07/ { jc AllocErr ;if any errors ....}π $C7/$46/$FE/$00/$00/ {NoErrors: mov Word [bp-2],0 ;Return 0 For successful allocation}π $EB/$05/ { jmp short Exit}π $89/$46/$FE/ {AllocErr: mov [bp-2],ax ;Return error code}π $31/$C0/ { xor ax,ax ;Store a NIL value into the ptr}π $C4/$7E/<PTR/ {Exit: les di,[bp+<Ptr] ;Address of Pointer into es:di}π $50/ { push ax ;Save the Segment part}π $31/$C0/ { xor ax,ax ;offset is always 0}π $FC/ { cld ;Make sure direction is upward}π $AB/ { stosw ;Store offset of memory block}π $58/ { pop ax ;Get back segment part}π $AB); { stosw ;Store segment of memory block}ππend {Malloc};ππ{ ---------------------------------------------------------------------------π}ππFunction Dalloc(Var Ptr) : Word;πbeginπ if Pointer(Ptr) <> NIL then beginπ Inline(π $B4/$49/ { mov ah,$49}π $C4/$7E/<PTR/ { les di,[bp+<Ptr]}π $26/$C4/$3D/ { es: les di,[di]}π $CD/$21/ { int $21}π $72/$02/ { jc Exit}π $31/$C0/ {NoError: xor ax,ax}π $89/$46/$FE); {Exit: mov [bp-2],ax}π Pointer(Ptr) := NIL;π end {if}π elseπ Dalloc := 0;πend {Dealloc};ππ{ ---------------------------------------------------------------------------π}ππFunction DosMemAvail : LongInt;πbeginπ Inline(π $BB/$FF/$FF/ { mov bx,$FFFF}π $B4/$48/ { mov ah,$48}π $CD/$21/ { int $21}π $89/$D8/ { mov ax,bx}π $B9/$10/$00/ { mov cx,16}π $F7/$E1/ { mul cx}π $89/$46/$FC/ { mov [bp-4],ax}π $89/$56/$FE); { mov [bp-2],dx}πend; {DosMemAvail}ππend. {Unit MemAlloc}ππ{Ok.. The Code can be rewritten to use GetMem and FreeMem (in fact I suggestπyou do this). I rewrote it myself to do so, but this is the distribution copy.π(I made one change in line 316-318 of SBVOICE.PAS bumping up the driver sizeπfrom 3000 to 5000 to accomodate the SoundBlaster 2.0 driver)πThis Program requires CT-VOICE.DRV which is distributed With the Soundblaster.π}π 10 05-28-9313:50ALL SWAG SUPPORT TEAM MEMINFO.PAS IMPORT 32 {πFirst of all something about Turbo Pascal memory management. A Turbo PascalπProgram Uses the upper part of the memory block it allocates as the heap.πThe heap is the memory allocated when using the Procedures 'New' andπ'GetMem'. The heap starts at the address location pointed to by 'Heaporg' andπgrows to higher addresses as more memory is allocated. The top of the heap,πthe first address of allocatable memory space above the allocated memoryπspace, is pointed to by 'HeapPtr'.ππMemory is deallocated by the Procedures 'Dispose' and 'FreeMem'. As memoryπblocks are deallocated more memory becomes available, but..... When a blockπof memory, which is not the top-most block in the heap is deallocated, a gapπin the heap will appear. to keep track of these gaps Turbo Pascal maintainsπa so called free list.ππThe Function 'MaxAvail' holds the size of the largest contiguous free blockπ_in_ the heap. The Function 'MemAvail' holds the sum of all free blocks inπthe heap.ππThus Far nothing has changed from TP5.5 to TP6.0. But here come theπdifferences:ππTP5.5ππto keep track of the free blocks in the heap, TP5.5 maintains a free listπwhich grows _down_ from the top of the heap. As more free blocks becomeπavailable, this list will grow. Every item in this list, a free-list Record,πcontains two four-Byte Pointers to the top and the bottom of a free blockπin the heap. _FreePtr_ points to the first free-list Record (the bottom mostπfree-list Record).ππThe minimum _allowable_ distance between 'FreePtr' and 'HeapPtr' can be setπwith the Variable 'FreeMin'.ππTP6.0ππIn TP6.0 the Variables 'FreePtr' and 'FreeMin' no longer exist. The free listπas implemented in TP5.5 no longer exists either (although the TP6.0πProgrammer's guide still mentions a down growing free list??)). TP6.0 keepsπtrack of the free blocks by writing a 'free list Record' to the first eightπBytes of the freed memory block! A (TP6.0) free-list Record contains two fourπByte Pointers of which the first one points to the next free memory block, theπsecond Pointer is not a Real Pointer but contains the size of the memory block.πSummaryππSo instead of a list of 'free list Records', growing down from the top of theπheap, containing Pointers to individual memory blocks, TP6.0 maintains a linkedπlist With block sizes and Pointers to the _next_ free block.πIn TP6.0 an extra heap Variable 'Heapend' designating the end of the heap isπadded. When 'HeapPtr' and 'FreeList' have the same value, the free list isπempty.ππThe below figure pictures the memory organization of both TP5.5 and TP6.0:πππ TP5.5 TP6.0 Heapendπ─── ┌─────────┐ ┌─────────┐ <────π ^ ┌──────│ │ │ │π │ │ ├─────────┤ │ │π │ │ ┌── │ │ FreePtr │ │π │ │ │ ├─────────┤ <──── │ │πHeap │ │ │ │ │ │π │ │ │ │ │ │ │π │ │ │ │ │ │ │π v │ │ │ │ HeapPtr │ │ HeapPtrπ─── │ │ ├─────────┤ <──── ┌─>├─────────┤ <────π │ │ │ │ │ │ │π │ ├──>├─────────┤ │ ├─────────┤π │ │ │ Free │ └──│ Free │π │ └──>├─────────┤ ┌─>├─────────┤π │ │ │ │ │ │π ├─────>├─────────┤ │ ├─────────┤π │ │ Free │ Heaporg └──│ Free │ FreeListπ └─────>├─────────┤ <──── ├─────────┤ <────π │ │ Heaporgπ ├─────────┤ <────ππππππI hope this will help you modifying existing toolBox's which make use of theseπdisappeared Variables. In some Case a modification may be quite easy, but asπyou see it might get quite quite difficult as well.π 11 05-28-9313:50ALL SWAG SUPPORT TEAM MEMINFO2.PAS IMPORT 10 π I need the proper syntax For a Pascal Program that will execute a Dosπ prog (a small one) and then resume the Pascal Program when the Dos progπ is finished. Any suggestions gladly accepted...ππ TP method:ππ Assumes Programe name is \PROGPATH\PROGNAME.EXE, and the commandπ line parameters are /Rππ Exec('\PROGPATH\PROGNAME.EXE','/R');ππ You need to make sure that you have the Heap set With the $Mπ directives, so that you have enough memory to execute theπ porgram.ππ example (this Program doesn't use the heap at all):ππ {$M 1024, 0, 0} { 1 kb stack, 0k min, 0k max }ππ (this Program needs 20k minimum heap to run, and can use up toπ 100k)ππ {$M 1024, 20480, 102400} { 1k stack, 20k min, 100k max }ππ A Turbo Pascal Program will always use as much RAM as there isπ avaiable, up to the "max" limit. if you do not put a $M directiveπ in your Program, the heap will be the entire available memory ofπ your machine, so no memory will be available For your externalπ Program to run.ππ It is also a good idea to bracket your Exec command withπ "SwapVector;" statements.π 12 05-28-9313:50ALL SWAG SUPPORT TEAM OVERSIZE.PAS IMPORT 45 Unit Oversize;ππ{ Author: Trevor J Carlsenπ Algorithm Enterprises Pty Ltdπ PO Box 568π Port Hedland 6721π Western Australiaπ Telephone: (Voice) +61 [0]91 73 2026π (Data ) +61 [0]91 73 2569π π Released into the Public Domain 1991.ππ An Unit that will enable logical Arrays to be created using up to the amount π of heap memory available.ππ The line marked (**) MUST be altered to reflect the Type of data in big π Array and the Unit MUST be reCompiled after this change.ππ No provision is made in this Unit For recovering the memory used by the big π Array as the intention was to keep it appearing to the Programmer as close π as possible to static Type global Variables.ππ Bear in mind that you do not declare your Array anywhere using this Unit. π That is all handled automatically. All you have to do is give the global π Variable MaxElements a value With the number of elements you want in the π Array and then call the Procedure initialise. From then on your Array is π called data^. (Actually it is called nothing as it is dynamic and is π referenced via the Pointer "data" but if you think of it as being called π "data^" you don't even need to know how Pointers work!)ππ The Array, using this Unit, can only be singly dimensioned although there is π no reason why the Unit could not be hacked to allow multi-dimensions.π π }ππInterfaceππTypeπ(**) DataType = LongInt; { change to whatever Type you want For the Array }π bigArray = Array[0..0] of DataType;π bigptr = ^bigArray;πVarπ data : bigptr;π MaxElements : LongInt; { this Variable must have the number of elements }ππ{----------------------------------------------------------------------------}πFunction Element(l:LongInt):Byte;π π { Call by passing the element number you wish to reference. }π { Always returns zero. It works by changing the value of the Pointer }π { data. This means that you cannot ever reference your big Array by }π { data^[100000] := whatever; }π { It MUST always be referenced by calling this Function eg. }π { data^[Element(100000)] := whatever; }π ππ{----------------------------------------------------------------------------}πFunction AllocateMem(Var b,l): Boolean;π π { Returns True if memory was allocated successfully For the big Array and }π { False if there was insufficient memory. }ππ{----------------------------------------------------------------------------}πProcedure Initialise; { Must be called beFore using any other Procedure }ππ{============================================================================}ππImplementationππ{============================================================================}π{ private declarations }ππConstπ max = 65520 div sizeof(dataType);{ The number of elements/segment }π initialised : Boolean = False;π πTypeπ address = Record { allows arithmetic on the Pointers }π offset,π segment : Word;π end;π baseArray = Array[0..9] of address; { For the addresses of the segments }ππVarπ base : baseArray;π ππ{----------------------------------------------------------------------------}πFunction Element(l:LongInt):Byte;ππ Varπ theaddress : address Absolute data;π bigaddress : baseArray Absolute base;ππ beginπ π { First make sure that initialisation has been done correctly }π if not initialised then begin π Writeln('Initialise Procedure has not been called');π halt(254);π end; { if not initialised }π π Element := 0; { It is Really irrelevent but any other value here would }π { produce a range check error at runtime if R+ }π π { Now let us fool TP into thinking that the address of element zero is }π { address of the element we are looking For. }π With theaddress do beginπ segment := bigaddress[l div max].segment; { Get the segment }π offset := (l mod max) * sizeof(dataType); { Get the offset }π end; { With theaddress }π end; { ElementNumber }ππ{----------------------------------------------------------------------------}πFunction AllocateMem(Var b,l): Boolean;π π Typeπ ptrArray = Array[0..9] of Pointer;π Varπ bArray: ptrArray Absolute b;π x : Byte;π count : LongInt;π beginπ count := MaxElements;π AllocateMem := True;π For x := 0 to (count div max) do { allocate in 64K contiguous chunks }π if (count * sizeof(dataType)) > 65520 then beginπ if MaxAvail < (max * sizeof(dataType)) then begin { not enough memory} π dec(count,max);π AllocateMem := False;π end π elseπ GetMem(bArray[x],max * sizeof(dataType));π endπ elseπ if MaxAvail < (count * sizeof(dataType)) thenπ AllocateMem := Falseπ elseπ GetMem(bArray[x],count * sizeof(dataType)); π end; { AllocateMem }π π{----------------------------------------------------------------------------}πProcedure Initialise;π beginπ FillChar(base,sizeof(base),0);π if not AllocateMem(base,MaxElements) then beginπ Writeln('Insufficient memory');π halt(255);π end;π initialised := True; { All Ok and memory has been allocated }π end; { Initialise }π πend. { Unit Oversize }ππ 13 05-28-9313:50ALL SWAG SUPPORT TEAM SDRVFLSH.PAS IMPORT 7 {πHAGEN LEHMANNππThis Procedure flushes the SMARTDRV.EXE-cache.π}ππProcedure FlushChache; Assembler;πAsmπ mov ax,$4A10π mov bx,$0002π int $2Fπend;ππ{πMARCO MILTENBURGππFlushing SmartDrive: It's written by Max Maischein (2:249/6.17) and NorbertπIgl (2:2402/300.3), both from Germany (if I'm not mistaken).π}ππProcedure FlushSD_sys; Far;πVarπ F : File;π B : Byte;πbeginπ Assign(F, 'SMARTAAR');π Reset(F);π B := 0;π Asmπ push dsπ mov ax, 04403hπ mov bx, FileRec(F).Handleπ mov cx, 1π int 21hπ pop dsπ end;πend;ππProcedure FlushSD_exe; Far;πbeginπ Asmπ mov ax, 04A10hπ mov bx, 1π int 2Fhπ end;πend;π 14 05-28-9313:50ALL SWAG SUPPORT TEAM SMARTDRV.PAS IMPORT 12 { MN> How can I find out if Smartdrv is installed ? I have made a harddiskπ MN> benchmark Program, and I would like it to detect if Smartdrv isπ MN> installed.π}πUses Dos;ππFunction SmartDrvVersion:Integer; { -1 means not inSTALLED }πVarπ R: Registers;π B: Array[0..$27] of Byte; { return Buffer }π F: Text;ππbeginπ SmartDrvVersion := -1; { assume NO smartdrv ! }ππ {--------Check For SmartDrv.EXE---------- }π FillChar( R, Sizeof(R), 0 );π R.AX := $4A10; { install-check }π Intr( $2F, R );π if R.FLAGS and FCARRY = 0 then { OK! }π beginπ if R.AX = $BABE then { the MAGIC-# }π beginπ SmartDrvVersion := Integer(R.BP);π Exitπ end;π end;π { -------Check For SmartDrv.SYS----------- }π Assign(f,'SMARTAAR');π {$I-}π Reset(f);π {$I+}π if IoResult <> 0 then Exit; { No SmartDrv }π FillChar( R, Sizeof(R), 0 );π R.AX := $4402; { IoCtl }π R.BX := TextRec(f).Handle;π R.CX := Sizeof(B);π R.DS := Seg(B);π R.DX := ofs(B);π MsDos(R); { int 21h }π close(f);π if R.FLAGS and FCARRY <> 0 then Exit; { Error-# in R.AX ...}π SmartDrvVersion := B[$E] + 256* B[$F];πend;ππVarπ SMV:Integer;πbeginπ SMV := SmartDrvVersion;π Write(' SmartDrv');π if SMV = -1 thenπ Writeln(' not installed.')π elseπ Writeln(' V', SMV div 256,'.',SMV mod 256,' installed.');πend.π 15 05-29-9322:20ALL GAYLE DAVIS Fast MOVE Replacement IMPORT 8 {$S-,R-,V-,I-,N-,B-,F-}ππ{$IFNDEF Ver40}π {Allow overlays}π {$F+,O-,X+,A-}π{$ENDIF}ππUNIT FastMove;ππINTERFACEππ(* This routine will move a block of data from a source to a destination. Itπ replaces Turbo Pascal's Move routine. *)ππPROCEDURE FastMover (VAR source;π VAR dest;π numToMove : WORD);πππIMPLEMENTATIONππPROCEDURE FastMover (VAR source;π VAR dest;π numToMove : WORD);ππ BEGINπ INLINE ($8C / $DA / $C5 / $B6 / > SOURCE / $C4 / $BE / > DEST / $8B / $8E / > NUMTOMOVE);π INLINE ($39 / $FE / $72 / $08 / $FC / $D1 / $E9 / $73 / $11 / $A4 / $EB / $0E / $FD / $01 / $CE);π INLINE ($4E / $01 / $CF / $4F / $D1 / $E9 / $73 / $01 / $A4 / $4E / $4F / $F2 / $A5 / $8E / $DA);π END;ππEND.π 16 05-29-9322:25ALL GAYLE DAVIS Extend HEAP to UMB IMPORT 74 {$A-,B-,D-,E-,F-,G-,I-,L-,N-,O-,R-,S+,V-,X-}ππUnit UMB_Heap;ππ{----------------------------------------------------------------------------}ππinterfaceππ Procedure Extend_Heap; { Use Upper Memory Blocks (UMB) to extend }π { the Turbo Pascal 6.0 heap. This procedure }π { should be called as soon as possible in }π { your code. }π varπ UMB_Heap_Debug : Boolean; { If true, releases UMBs immediately to make }π { sure they're available for the next run }π { without rebooting. Used when debugging in }π { the IDE. If not used then, the UMBs may }π { not get freed between executions. }ππ{----------------------------------------------------------------------------}ππimplementationππconstπ Max_Blocks = 4; { It's not likely more than 4 UMBs are needed }ππtypeπ PFreeRec = ^TFreeRec; { From pg. 216 of the TP6 programmer's guide. }π TFreeRec = record { It's used for traversing the free blocks of }π Next : PFreeRec; { the heap. }π Size : Pointer;π end;ππvarπ XMS_Driver : Pointer; { Pointer to the XMS driver. }π Num_Blocks : Word;π Block_Address,π Block_Size : Array[1..Max_Blocks+1] of Pointer;π SaveExitProc : Pointer;ππ{----------------------------------------------------------------------------}ππ{ Swap to pointers. Needed when sorting the UMB addresses. }ππProcedure Pointer_Swap(var A,B : Pointer);π varπ Temp : Pointer;π Beginπ Temp := A;π A := B;π B := Temp;π End;ππ{----------------------------------------------------------------------------}ππFunction XMS_Driver_Present : Boolean; { XMS software present? }π varπ Result : Boolean;π Beginπ Result := False; { Assume no XMS driver }π asmπ @Begin:π mov ax,4300hπ int 2Fhπ cmp al,80hπ jne @Failπ mov ax,4310hπ int 2Fhπ mov word ptr XMS_Driver+2,es { Get the XMS driver entry point }π mov word ptr XMS_Driver,bxπ mov Result,1π jmp @Endπ @Fail:π mov Result,0π @End:π end;π XMS_Driver_Present := Result;π End;ππ{----------------------------------------------------------------------------}ππProcedure Allocate_UMB_Heap; { Add the four largest UMBs to the heap }π varπ i,j : Word;π UMB_Strategy,π DOS_Strategy,π Segment,Size : Word;π Get_Direct : Boolean; { Get UMB direct from XMS if TRUE, else from DOS }π Beginπ Num_Blocks := 0;ππ for i := 1 to Max_Blocks doπ beginπ Block_Address[i] := nil;π Block_Size[i] := nil;π end;ππ asmπ mov ax,5800hπ int 21h { Get and save the DOS allocation strategy }π mov [DOS_Strategy],axπ mov ax,5802hπ int 21h { Get and save the UMB allocation strategy }π mov [UMB_Strategy],axπ mov ax,5801hπ mov bx,0000hπ int 21h { Set the DOS allocation strategy so that }π mov ax,5803h { it uses only high memory }ππ { DON'T TRUST THIS FUNCTION. DOS WILL GO }π { AHEAD AND TRY TO ALLOCATE LOWER MEMORY }π { EVEN AFTER YOU TELL IT NOT TO! }π mov bx,0001hπ int 21h { Set the UMB allocation strategy so that }π end; { UMBs are added to the DOS mem chain }ππ Get_Direct := True; { Try to get UMBs directly from the XMS }π { if possible. }π for i := 1 to Max_Blocks doπ beginπ Segment := 0;π Size := 0;ππ if Get_Direct then { Get a UMB direct from the XMS driver. }π beginπ asmπ @Begin:π mov ax,01000h π mov dx,0FFFFh { Ask for the impossible to ... }π push ds { Get the size of the next largest UMB }π mov cx,dsπ mov es,cxπ call es:[XMS_Driver]π cmp dx,100h { Don't bother with anything < 1K }π jl @Endπ mov ax,01000hπ call es:[XMS_Driver] { Get the next largest UMB }π cmp ax,1π jne @Endπ cmp bx,0A000h { It better be above 640K }π jl @End { We can't trust DOS 5.00 }π mov [Segment],bxπ mov [Size],dxπ @End:π pop dsπ end;π if ((i = 1) and (Size = 0)) then { if we couldn't get the UMB }π Get_Direct := False; { from the XMS driver, don't }π end; { try again the next time. }ππ if (not Get_Direct) then { Get a UMB via DOS }π beginπ asmπ @Begin:π mov ax,4800hπ mov bx,0FFFFh { Ask for the impossible to ... }π int 21h { Get the size of the next largest UMB }π cmp bx,100h { Don't bother with anything < 1K }π jl @Endπ mov ax,4800hπ int 21h { Get the next largest UMB }π jc @Endπ cmp ax,0A000h { It better be above 640K }π jl @End { We can't trust DOS 5.00 }π mov [Segment],axπ mov [Size],bxπ @End:π end;π end;ππ if (Segment > 0) then { Did it work? }π beginπ Block_Address[i] := Ptr(Segment,0);π Inc(Num_Blocks);π end;π Block_Size[i] := Ptr(Size,0);π end;π if (Num_Blocks > 0) then { Sort the UMB addrs in ASC order }π beginπ for i := 1 to Num_Blocks-1 doπ for j := i+1 to Num_Blocks doπ if (Seg(Block_Address[i]^) > Seg(Block_Address[j]^)) thenπ beginπ Pointer_Swap(Block_Address[i],Block_Address[j]);π Pointer_Swap(Block_Size[i],Block_Size[j]);π end;π end;π asmπ mov ax,5803hπ mov bx,[UMB_Strategy]π int 21h { Restore the UMB allocation strategy }π mov ax,5801hπ mov bx,[DOS_Strategy]π int 21h { Restore the DOS allocation strategy }π end;π End;ππ{----------------------------------------------------------------------------}ππProcedure Release_UMB; far; { Exit procedure to release UMBs }π varπ i : Word;π Segment : Word;π Beginπ ExitProc := SaveExitProc;π if (Num_Blocks > 0) thenπ beginπ asmπ mov ax,5803hπ mov bx,0000hπ int 21h { Set the UMB status to release UMBs }π end;π for i := 1 to Num_Blocks doπ beginπ Segment := Seg(Block_Address[i]^);π if (Segment > 0) thenπ asmπ mov ax,$4901π mov bx,[Segment]π mov es,bxπ int 21h { Release the UMB }π end;π end;π end;π End;ππ{----------------------------------------------------------------------------}ππProcedure Extend_Heap;π varπ i : Word;π Temp : PFreeRec;π Beginπ if XMS_Driver_Present thenπ beginπ Allocate_UMB_Heap;π if UMB_Heap_Debug thenπ Release_UMB;π if (Num_Blocks > 0) thenπ begin { Attach UMBs to the FreeList }π for i := 1 to Num_Blocks doπ PFreeRec(Block_Address[i])^.Size := Block_Size[i];π for i := 1 to Num_Blocks doπ PFreeRec(Block_Address[i])^.Next := Block_Address[i+1];ππ PFreeRec(Block_Address[Num_Blocks])^.Next := nil;ππ if (FreeList = HeapPtr) thenπ with PFreeRec(FreeList)^ doπ beginπ Next := Block_Address[1];π Size := Ptr(Seg(HeapEnd^)-Seg(HeapPtr^),0);π endπ elseπ with PFreeRec(HeapPtr)^ doπ beginπ Next := Block_Address[1];π Size := Ptr(Seg(HeapEnd^)-Seg(HeapPtr^),0);π end;ππ { HEAPPTR MUST BE IN THE LAST FREE BLOCK SOπ THAT TP6 DOESN'T TRY TO USE ANY MEMORY BETWEENπ 640K AND HEAPPTR }ππ HeapPtr := Block_Address[Num_Blocks];π HeapEnd := Ptr(Seg(Block_Address[Num_Blocks]^)+Seg(Block_Size[Num_Blocks]^),0);π end;π end;π End;ππ{----------------------------------------------------------------------------}ππBEGINπ UMB_Heap_Debug := False;π Num_Blocks := 0;π SaveExitProc := ExitProc;π ExitProc := @Release_UMB;πEND.ππ{----------------------------------------------------------------------------}π